<!DOCTYPE html>
<html>
  <head>
    <style>
      body {
        background-color: #222;
        margin: 0;
        padding: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <div id="i-container">
      <canvas id="i-frostpad-canvas"></canvas>
    </div>

    <script src="src/WAAX.js"></script>
    <script src="src/Fader.js"></script>
    <script src="src/Noise.js"></script>
    <script src="src/FilterBank.js"></script>
    <script src="src/ADSR.js"></script>
    <script src="src/Chorus.js"></script>
    <script src="src/StereoVisualizer.js"></script>

    <script>
      (function () {


        /**
         * Synth
         */
        var n = WX.Noise();
        var fb = WX.FilterBank();
        var env = WX.ADSR();
        var cho = WX.Chorus();
        var viz = WX.StereoVisualizer();

        env.setADSR(0.25, 0.05, 0.75, 1.5);
        env.setParam('pMode', 'monophonic');

        WX.patch = n >> fb >> env >> cho >> WX.Dac;
        WX.patch = cho >> viz;

        var pitches = [31, 35, 36, 40, 41]; // g, b, c, e, f

        function updateSynth(pos) {
          var pitch = pitches[Math.ceil(pos.nx * 5) - 1];
          var chord = "lydian";
          if (pos.ny < 0.33) {
            chord = 'mixolydian'
          } else if (pos.ny < 0.66) {
            chord = 'ionian';
          }
          fb.setParam('pPitch', pitch);
          fb.setParam('pChord', chord);
          fb.setParam('pSlope', pos.nx, 1, WX.now+0.1);
          fb.setParam('pWidth', pos.ny, 1, WX.now+0.1);
        }


        /**
         * Canvas2D
         */
        var cvs = document.getElementById('i-frostpad-canvas');
        var ctx = cvs.getContext('2d');

        // init
        var width = window.innerWidth;
        var height = window.innerHeight;
        cvs.width = width;
        cvs.height = height;

        // interaction
        var pos = {}, timeout = null, inactive = true;
        function getMousePosition(event) {
          var box = cvs.getBoundingClientRect();
          var x = event.clientX - box.left;
          var y = event.clientY - box.top;
          return {
            x: x,
            y: y,
            nx: x / width,
            ny: y / height,
          };
        }

        function dragged(event) {
          pos = getMousePosition(event);
          updateSynth(pos);
        }

        function released(event) {
          env.noteOff();
          timeout = setTimeout(function () {
            inactive = true;
          }, 1000);
          window.removeEventListener('mousemove', dragged, false);
          window.removeEventListener('mouseup', released, false);
        }

        cvs.addEventListener('mousedown', function (event) {
          env.noteOn();
          inactive = false;
          if (timeout) {
            clearTimeout(timeout);
          }
          pos = getMousePosition(event);
          updateSynth(pos);
          window.addEventListener('mousemove', dragged, false);
          window.addEventListener('mouseup', released, false);
        }, false);

        function ontouchstart(event) {
          event.preventDefault();
          inactive = false;
          if (timeout) {
            clearTimeout(timeout);
          }
          var touch = event.touches[0];
          pos = getMousePosition(touch);
          updateSynth(pos);
          env.noteOn();
        }

        function ontouchmove(event) {
          event.preventDefault();
          var touch = event.touches[0];
          pos = getMousePosition(touch);
          updateSynth(pos);
        }

        function ontouchend(event) {
          event.preventDefault();
          env.noteOff();
          timeout = setTimeout(function () {
            inactive = true;
          }, 1000);
        }

        function fullscreen() {
          if (cvs.webkitRequestFullScreen) cvs.webkitRequestFullScreen();
          else if (cvs.requestFullscreen) cvs.requestFullscreen();
          else if (cvs.mozRequestFullScreen) cvs.mozRequestFullScreen();
        }

        function resize() {
          var w = Math.ceil(window.innerWidth);
          var h = Math.ceil(window.innerHeight);
          var a = w / h;
          cvs.style.width = window.innerWidth + "px";
          cvs.style.height = window.innerHeight + "px";
          cvs.style.zIndex = 1001;
          cvs.style.position = 'fixed';
          cvs.style.top = 0;
        }

        cvs.addEventListener("touchstart", ontouchstart, false);
        cvs.addEventListener("touchmove", ontouchmove, false);
        cvs.addEventListener("touchend", ontouchend, false);

        window.addEventListener("resize", resize);
        cvs.addEventListener("touchstart", fullscreen, false);

        /**
         * Drawing parameters
         */
        ctx.fillStyle = "rgba(16, 32, 16, 0.25)";
        ctx.strokeStyle = "rgba(16, 255, 16, 0.5)";
        ctx.lineWidth = 0.5;

        var gridFactor = 32;
        var gx = width / gridFactor;
        var gy = height / gridFactor;
        var scaleX = width * gridFactor / 8.0;
        var scaleY = height * gridFactor / 8.0;

        // Quantized-lissajou pattern
        viz.onDraw(function (buffer1, buffer2) {
          ctx.fillRect(0, 0, width, height);
          if (inactive) {
            return;
          }
          ctx.save();
          ctx.translate((pos.x) - (pos.x % gx), (pos.y) - (pos.y % gy));
          ctx.beginPath();
          for (var i = 0; i < buffer1.length - 1; i++) {
            var x1 = (buffer1[i] / 128 - 1.0) * scaleX;
            var y1 = (buffer2[i] / 128 - 1.0) * scaleY
            var x2 = (buffer1[i+1] / 128 - 1.0) * scaleX;
            var y2 = (buffer2[i+1] / 128 - 1.0) * scaleY
            if (Math.random() > 0.5) {
              ctx.moveTo(x1, y1);
              ctx.quadraticCurveTo(x1, y2, x2, y2);
            } else {
              ctx.moveTo(-x1, y1);
              ctx.quadraticCurveTo(-x1, y2, -x2, y2);
            }
          }
          ctx.stroke();
          ctx.restore();
        });

        function loop() {
          viz.drawWaveform();
          requestAnimationFrame(loop);
        }
        loop();

      })();
    </script>
  </body>
</html>